本篇主要檢視Go語言標準套件本身的程式碼片段,來了解Go錯誤處理的慣例,以及如何建立自訂錯誤error。
大部分程式語言(如筆者我Java仔),如果某些功能有發生錯誤的可能性,我們就得寫try....catch去包住,但在Go中很明確的會先接收到error值,然後自己判斷該對他做些啥事。
val err := someFunc() //呼叫函式,接收回傳值(包括error)
if err != nil {
//若有錯誤存在,做些處理然後把它繼續往外傳
return err
}
return nil //沒有錯誤,對上一層回nil
如果只是要檢查一個函式是否正確執行,可以寫如下 簡潔一點:
if _,err := someFunc(); err != nil {
}
Go 語言藉由這種方式,明白要求開發者,承擔起處理錯誤的責任,不僅簡化了檢查錯誤的流程,也讓開發者付出更多心力在關注程式碼的潛在問題。
接下來我們來瞧瞧Go語言中的錯誤值到底是啥。
在 Go 語言中,一個error是一個值。
Go語言創始人 Rob Pike 對 error 是這樣形容 :
資料值是能用程式設定的,而既然error也是值,所以,error 同樣可以用程式設定內容。
error 不是程式例外,他並無任何特別之處,因為未處理的例外會讓程式當掉,但error卻不見得。
既然error是一個值,那麼就可以把它當作引數傳給函式、被函式傳回,並能像Go語言中任何值依樣被取出來做比較。
Go 語言的error值都必須實作來符合error介面的定義(介面實作會在後續篇幅說明),以下是Go的error介面 :
type error interface(){
Error() string
}
**Note : 這個型別被宣告在最高層級,任何Go程式都可以直接取用,一個型別只要符合介面的規範,就會被視作為符合該介面型別,也就是 : 任何型別只要符合error介面的要求,就能當作error型別: **
* 型別得要擁有一個函式(或方法)叫做 Error()
由以下程式內的錯誤處理,來說明Go如何處理錯誤 :
package main
import (
"fmt"
"strconv"
)
func main() {
v := "10"
s, err := strconv.Atoi(v)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%T, %v\n", s, s)
v = "s2"
s2, err := strconv.Atoi(v)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%T, %v\n", s2, s)
}
從上面看到,Go標準函式庫strconv.Atoi()會接收一個字串,並遵循Go語言的設計慣例回傳一個int,以及一個error值。任何函式若要回傳錯誤,error應該要當作最後一個傳回值。
簡單的複習下,可以注意到main()中err變數是重複使用的,我們之前有提到短變數宣告,只要左側其中只少有一新變數既可成立,範例中 (s, err)、(s2, err)。
若函釋回傳error 還不理它,是很糟糕 ^ ^ 的習慣,之後debug痛苦na。
error 值是nil代表沒有錯誤,但若不是nil就是有了,因應場合不同,我們可以 :
來研究Go語言標準套件的error型別,我們先從error.go開始
// https://golang.org/src/errors/errors.go
type errorString struct {
s string
}
errorString 這個結構位於 error 套件中,裡面有一個字串型別的欄位 s,來儲存錯誤內容。注意到 errorString 型別和其欄位 s 都是以小寫開頭,代表他們不是公開的,也就代表我們不能在外部程式直接使用它。
package main
import (
"errors"
"fmt"
)
func main() {
es := errors.errorString{}
es.s = "slacker"
fmt.Println(es)
}
但是呢,error.go中還有這麼一段 :
// https://golang.org/src/errors/errors.go
type errorString struct {
s string
}
//這一段
func (e *errorString) Error() string {
return e.s
}
Error()函式,透過指標接收器變成errorString結構的方法,這樣一來errorString結構定義就符合error介面要求,使errorString結構可以被視為error型別。
而如果你需要查看錯誤內容,只需要呼叫Error()方法和取得傳回字串即可,這意味著你可以用各式各樣方法定義error值,而它的型別只要符合error介面,你就能用完全一樣的方法操作。
接下來我們會在下一篇來建立自訂error值~